package com.strato.orm.mybatis.v1_0_0.annotation.handler;

import java.lang.reflect.Method;
import java.sql.Connection;

import javax.sql.DataSource;

import org.apache.ibatis.binding.BindingException;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.osgi.framework.Bundle;

import com.strato.base.aop.v1_0_0.Invocation;
import com.strato.base.aop.v1_0_0.InvocationInterceptor;
import com.strato.base.osgi.v1_0_0.BundleUtil;
import com.strato.jdbc.datasource.v1_0_0.DataSourceFactory;
import com.strato.orm.mybatis.v1_0_0.activator.Activator;
import com.strato.transaction.v1_0_0.api.TransactionStatus;
import com.strato.transaction.v1_0_0.api.TransactionStatusHolder;

public class MybatisMapperInvocationInterceptor implements InvocationInterceptor {

	private String fieldType;
	private String bundleName;
	private String dataSourceName;

	public MybatisMapperInvocationInterceptor(String bundleName, String fieldType,String dataSourceName) {
		this.bundleName=bundleName;
		this.fieldType=fieldType;
		this.dataSourceName=dataSourceName;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Method method = invocation.getMethod();
		Object[] args = invocation.getArgs();
		TransactionStatus txStatus = TransactionStatusHolder.get();
		DataSource dataSource=DataSourceFactory.getInstance().getDataSource(dataSourceName);
		boolean inTx=txStatus!=null;
		Connection conn=null;
		if(inTx){
			conn=txStatus.getConnection(dataSource);
		}else{
			conn=dataSource.getConnection();
		}
		Bundle bundle=BundleUtil.getBundle(bundleName, Activator.getContext());
		SqlSession session = null;
		try {
			Class<?> mapperType = bundle.loadClass(fieldType);
			session=getSessionForMapper(mapperType,conn);
			Object mapper = session.getMapper(mapperType);
			Method wrappedMethod=mapper.getClass().getMethod(method.getName(), method.getParameterTypes());
			return wrappedMethod.invoke(mapper, args);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}finally{
			try{
				session.close();
			}catch(Exception e){
				e.printStackTrace();
			}
			if(!inTx){
				conn.close();
			}
		}
	}

	private SqlSession getSessionForMapper(Class<?> mapperType, Connection conn) {
		int tryTimes=10;
		for(int i=0;i<tryTimes;i++){
			SqlSessionFactory sqlSessionFactory = Activator.getProvider().provide();
			SqlSession session = sqlSessionFactory.openSession(conn);
			Object mapper=null;
			try{
				mapper = session.getMapper(mapperType);
				if(mapper!=null){
					return session;
				}
			}catch(BindingException be){
				System.out.println("Retrying get mapper for type:"+mapperType);
			}finally{
				if(mapper==null){
					session.close();
				}
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
}
